package com.ikas.ai.server.module.steady.service;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.ikas.ai.enums.redis.RedisKeyEnum;
import com.ikas.ai.model.PageQuery;
import com.ikas.ai.server.error.BusinessException;
import com.ikas.ai.server.module.appraise.enums.StatusEnum;
import com.ikas.ai.server.module.data.dao.MeteDao;
import com.ikas.ai.server.module.data.model.BaseMete;
import com.ikas.ai.server.module.steady.dao.SteadyAlarmDataMapper;
import com.ikas.ai.server.module.steady.dao.SteadyMonitorMeteMapper;
import com.ikas.ai.server.module.steady.enums.AlarmConditionEnum;
import com.ikas.ai.server.module.steady.enums.SteadyMeteTypeEnum;
import com.ikas.ai.server.module.steady.model.dto.*;
import com.ikas.ai.server.module.steady.model.po.SteadyMonitorMete;
import com.ikas.ai.server.module.steady.model.po.SteadyStateMete;
import com.ikas.ai.server.module.steady.model.vo.StyMonMeteVo;
import com.ikas.ai.utils.PageExUtil;
import com.ikas.ai.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 稳态监测测点配置表 服务实现类
 * </p>
 *
 * @author wuyf
 * @since 2023-12-13
 */
@Slf4j
@Service
public class SteadyMonitorMeteService extends ServiceImpl<SteadyMonitorMeteMapper, SteadyMonitorMete> {

    @Resource
    private SteadyStateMeteService steadyStateMeteService;
    @Resource
    private SteadyAlarmDataMapper steadyAlarmDataMapper;
    /**
     * 本地缓存时间 （位：毫秒）
     */
    @Value("${local-cache-timeout:3600000}")
    private Long localCacheTimeout;

    /**
     * 本地缓存
     */
    private TimedCache<String, Map<String, Integer>> localTimeCache;

    public TimedCache<String, Map<String, Integer>> getLocalTimeCache() {
        if (localTimeCache == null) {
            localTimeCache = CacheUtil.newTimedCache(localCacheTimeout);
        }
        return localTimeCache;
    }


    @Resource
    private MeteDao meteDao;

    public PageInfo<StyMonMeteVo> pageStyMonMete(StyMonMeteQueryDto queryDto, PageQuery page) {
        PageExUtil.configPageHelper(page);
        List<SteadyMonitorMete> list = queryWrapperList(queryDto);
        return PageExUtil.convert(new PageInfo<>(list), this::coverToStyMonMeteVo);
    }


    public List<StyMonMeteVo> listStyMonMete(StyMonMeteQueryDto queryDto) {
        List<SteadyMonitorMete> list = queryWrapperList(queryDto);
        return list.stream().map(this::coverToStyMonMeteVo).collect(Collectors.toList());
    }


    public List<SteadyMonitorMete> listStyMonMeteNoVo(StyMonMeteQueryDto queryDto) {
        return queryWrapperList(queryDto);
    }

    private List<SteadyMonitorMete> queryWrapperList(StyMonMeteQueryDto queryDto) {
        LambdaQueryWrapper<SteadyMonitorMete> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getSteadyType()), SteadyMonitorMete::getSteadyType, queryDto.getSteadyType());
        queryWrapper.eq(StringUtils.isNotBlank(queryDto.getMachineNo()), SteadyMonitorMete::getMachineNo, queryDto.getMachineNo());
        queryWrapper.eq(StringUtils.isNotBlank(queryDto.getMeteCode()), SteadyMonitorMete::getMeteCode, queryDto.getMeteCode());
        queryWrapper.like(StringUtils.isNotBlank(queryDto.getMeteName()), SteadyMonitorMete::getMeteName, queryDto.getMeteName());
        queryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getMonitorType()), SteadyMonitorMete::getMonitorType, queryDto.getMonitorType());
        queryWrapper.eq(ObjectUtils.isNotEmpty(queryDto.getStatus()), SteadyMonitorMete::getStatus, queryDto.getStatus());
        queryWrapper.orderByAsc(SteadyMonitorMete::getMachineNo).orderByDesc(SteadyMonitorMete::getCreateTime);
        return this.list(queryWrapper);
    }

    /**
     * 实体转VO
     *
     * @param styMonMete 实体类
     * @return vo of StyMonMete
     */
    private StyMonMeteVo coverToStyMonMeteVo(SteadyMonitorMete styMonMete) {
        return BeanUtil.copyProperties(styMonMete, StyMonMeteVo.class);
    }


    @Transactional(rollbackFor = Exception.class)
    public boolean addStyMonMete(StyMonMeteSaveDto styMonMeteDto) {
        SteadyMonitorMete smm = BeanUtil.copyProperties(styMonMeteDto, SteadyMonitorMete.class);
        this.save(smm);
        //新增时因为status为0，未生效的数据不用重新同步到redis中
//        reSetValueOfRedis();
        return true;
    }


    @Transactional(rollbackFor = Exception.class)
    public boolean updateStyMonMete(StyMonMeteUpdateDto styMonMeteDto) {
        SteadyMonitorMete smm = this.getById(styMonMeteDto.getId());
        if (smm == null) {
            throw new BusinessException("没找到可修改的数据");
        }
        if (StatusEnum.VALID.getCode().equals(smm.getStatus())) {
            throw new BusinessException("当前数据已生效，不能修改");
        }
        SteadyMonitorMete newSmm = BeanUtil.copyProperties(styMonMeteDto, SteadyMonitorMete.class);
        this.updateById(newSmm);
        //重置redis的值
        RedisUtils.del(RedisKeyEnum.STEADY_MONITOR_METE_CODE.key(styMonMeteDto.getMeteCode()));
        reSetValueOfRedis();
        return true;
    }


    public SteadyMonitorMete detail(Long id) {
        return this.getById(id);
    }


    @Transactional(rollbackFor = Exception.class)
    public boolean enableStyMonMete(Long id) {
        SteadyMonitorMete smm = this.getById(id);
        if (smm == null) {
            throw new BusinessException("没有找到对应的数据");
        }
        SteadyMonitorMete newSmm = new SteadyMonitorMete();
        newSmm.setId(smm.getId());
        //异或运算一下
        newSmm.setStatus(smm.getStatus() ^ 1);
        this.updateById(newSmm);
        //重置redis的值
        RedisUtils.del(RedisKeyEnum.STEADY_MONITOR_METE_CODE.key(smm.getMeteCode()));
        reSetValueOfRedis();
        return true;
    }


    @Transactional(rollbackFor = Exception.class)
    public boolean delStyMonMete(Long id) {
        SteadyMonitorMete smm = this.getById(id);
        if (smm == null) {
            throw new BusinessException("没有找到需要删除的数据");
        }
        if (smm.getStatus().equals(StatusEnum.VALID.getCode())) {
            throw new BusinessException("生效状态，不能删除");
        }
        int count = steadyAlarmDataMapper.selectCountByMonitorMete(Lists.newArrayList(id));
        if (count > 0) {
            throw new BusinessException("存在预警数据，不能删除该配置");
        }
        this.removeById(id);
        //重置redis的值
        RedisUtils.del(RedisKeyEnum.STEADY_MONITOR_METE_CODE.key(smm.getMeteCode()));
        reSetValueOfRedis();
        return true;
    }


    @Transactional(rollbackFor = Exception.class)
    public boolean delBatchStyMonMete(List<Long> ids) {
        List<SteadyMonitorMete> styMonMetes = this.listByIds(ids);
        List<SteadyMonitorMete> validCollect = styMonMetes.stream().filter(mete -> mete.getStatus().equals(StatusEnum.VALID.getCode())).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(validCollect)) {
            throw new BusinessException("包含生效状态的数据，不能批量删除");
        }
        int count = steadyAlarmDataMapper.selectCountByMonitorMete(ids);
        if (count > 0) {
            throw new BusinessException("存在预警数据，不能删除该配置");
        }
        this.removeByIds(ids);
        Set<String> redisKeyList = styMonMetes.stream().map(mete ->
                RedisKeyEnum.ASSESS_MODEL_TRIGGER_KEY.key(mete.getMeteCode())
        ).collect(Collectors.toSet());
        //重置redis的值
        RedisUtils.del(redisKeyList);
        reSetValueOfRedis();
        return true;
    }

    /**
     * 重置redis的值
     */
    public synchronized void reSetValueOfRedis() {
        List<SteadyStateMete> stateMetes = steadyStateMeteService.listStyStateMete(new StyStateMeteQueryDto());
        StyMonMeteQueryDto styMonMeteQueryDto = new StyMonMeteQueryDto();
        styMonMeteQueryDto.setStatus(StatusEnum.VALID.getCode());
        List<SteadyMonitorMete> monitorMetes = listStyMonMeteNoVo(styMonMeteQueryDto);
        Map<String, Integer> map = new HashMap<>(16);
        Map<String, Integer> stateMeteMap = stateMetes.stream().collect(
                Collectors.toMap(SteadyStateMete::getMeteCode,
                        m -> SteadyMeteTypeEnum.STATE_METE.getValue(), (key1, key2) -> key2)
        );
        Map<String, Integer> monitorMeteMap = monitorMetes.stream().collect(
                Collectors.toMap(SteadyMonitorMete::getMeteCode,
                        m -> SteadyMeteTypeEnum.MONITOR_METE.getValue(), (key1, key2) -> key2)
        );
        map.putAll(stateMeteMap);
        map.putAll(monitorMeteMap);
        log.info("重置redis中key值：{}，size:{}", RedisKeyEnum.STEADY_METE_CODE.key(), map.size());
        RedisUtils.del(RedisKeyEnum.STEADY_METE_CODE.key());
        RedisUtils.set(RedisKeyEnum.STEADY_METE_CODE.key(), map);
        //本地缓存置为空
        TimedCache<String, Map<String, Integer>> localTimeCache = getLocalTimeCache();
        localTimeCache.put(RedisKeyEnum.STEADY_METE_CODE.key(), new HashMap<>());
    }

    public void importMete(MultipartFile file, Integer index) throws Exception {
        ImportParams importParams = new ImportParams();
        importParams.setStartSheetIndex(index);
        List<SteadyMonitiorMeteExcelDTO> dtoList = ExcelImportUtil.importExcel(file.getInputStream(), SteadyMonitiorMeteExcelDTO.class, importParams);
        log.info("导入的稳态类型测点数据:{}", JSON.toJSONString(dtoList));
        List<String> meteCodeList = dtoList.stream().map(SteadyMonitiorMeteExcelDTO::getMeteCode).collect(Collectors.toList());
        LambdaQueryChainWrapper<BaseMete> wrapper = new LambdaQueryChainWrapper<>(meteDao);
        List<BaseMete> baseMeteList = wrapper.in(BaseMete::getMeteCode, meteCodeList).list();
        Map<String, BaseMete> baseMeteMap = baseMeteList.stream().collect(Collectors.toMap((m -> m.getParentNodeName() + "_" + m.getMeteCode()), f -> f));
        List<SteadyMonitorMete> steadyMonitorMeteList = dtoList.stream().map(i -> {
            BaseMete baseMete = baseMeteMap.get(i.getParentNode() + "_" + i.getMeteCode());
            SteadyMonitorMete steadyMonitorMete = new SteadyMonitorMete();
            steadyMonitorMete.setMachineNo(baseMete.getMachineNo());
            steadyMonitorMete.setMeteCode(baseMete.getMeteCode());
            steadyMonitorMete.setMeteId(baseMete.getMeteId());
            steadyMonitorMete.setMeteName(i.getMeteName());
            steadyMonitorMete.setSteadyType("运行稳态".equals(i.getSteadyType()) ? 1 : 0);
            steadyMonitorMete.setMonitorType("阈值监测预警".equals(i.getMonitorType()) ? 0 : 1);
            steadyMonitorMete.setMonitorDesc(i.getCondition());
            if (0 == steadyMonitorMete.getMonitorType()) {
                steadyMonitorMete.setDelayAlarmDuration(Integer.valueOf(i.getTime()));
            } else {
                steadyMonitorMete.setTrendDuration(Integer.valueOf(i.getTime()));
            }
            AlarmConditionEnum conditionEnum = AlarmConditionEnum.getEnumByDesc(i.getAlarmCondition());
            steadyMonitorMete.setAlarmCondition(conditionEnum.getValue());
            steadyMonitorMete.setAlarmThreshold(Double.valueOf(i.getAlarmValue()));
            steadyMonitorMete.setStatus(1);
            steadyMonitorMete.setCreateTime(new Date());
            steadyMonitorMete.setUpdateTime(new Date());
            steadyMonitorMete.setCreator(1L);
            steadyMonitorMete.setUpdater(1L);
            return steadyMonitorMete;
        }).collect(Collectors.toList());
        saveBatch(steadyMonitorMeteList);
    }
}
